View Javadoc
1 /* 2 * $Header: /home/cvs/jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/io/AbstractBeanWriter.java,v 1.6 2002/10/14 01:55:38 dion Exp $ 3 * $Revision: 1.6 $ 4 * $Date: 2002/10/14 01:55:38 $ 5 * 6 * ==================================================================== 7 * 8 * The Apache Software License, Version 1.1 9 * 10 * Copyright (c) 1999-2002 The Apache Software Foundation. All rights 11 * reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in 22 * the documentation and/or other materials provided with the 23 * distribution. 24 * 25 * 3. The end-user documentation included with the redistribution, if 26 * any, must include the following acknowlegement: 27 * "This product includes software developed by the 28 * Apache Software Foundation (http://www.apache.org/)." 29 * Alternately, this acknowlegement may appear in the software itself, 30 * if and wherever such third-party acknowlegements normally appear. 31 * 32 * 4. The names "The Jakarta Project", "Commons", and "Apache Software 33 * Foundation" must not be used to endorse or promote products derived 34 * from this software without prior written permission. For written 35 * permission, please contact apache@apache.org. 36 * 37 * 5. Products derived from this software may not be called "Apache" 38 * nor may "Apache" appear in their names without prior written 39 * permission of the Apache Group. 40 * 41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 42 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 43 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 44 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 45 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 46 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 47 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 48 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 49 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 50 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 51 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 52 * SUCH DAMAGE. 53 * ==================================================================== 54 * 55 * This software consists of voluntary contributions made by many 56 * individuals on behalf of the Apache Software Foundation. For more 57 * information on the Apache Software Foundation, please see 58 * <http://www.apache.org/>;. 59 * 60 * $Id: AbstractBeanWriter.java,v 1.6 2002/10/14 01:55:38 dion Exp $ 61 */ 62 package org.apache.commons.betwixt.io; 63 64 import java.beans.IntrospectionException; 65 import java.io.BufferedWriter; 66 import java.io.IOException; 67 import java.io.OutputStream; 68 import java.io.OutputStreamWriter; 69 import java.io.Writer; 70 import java.util.Iterator; 71 import java.util.HashMap; 72 73 import org.apache.commons.logging.Log; 74 import org.apache.commons.logging.LogFactory; 75 76 import org.apache.commons.betwixt.AttributeDescriptor; 77 import org.apache.commons.betwixt.ElementDescriptor; 78 import org.apache.commons.betwixt.XMLBeanInfo; 79 import org.apache.commons.betwixt.XMLIntrospector; 80 import org.apache.commons.betwixt.expression.Context; 81 import org.apache.commons.betwixt.expression.Expression; 82 import org.apache.commons.betwixt.io.id.SequentialIDGenerator; 83 84 import org.xml.sax.SAXException; 85 86 // FIX ME!!! 87 // Logging logic! 88 89 // FIX ME!! 90 // Error handling strategy! 91 // i'm going to add SAXExceptions everywhere since it's the easiest way to make things work quick 92 // but this is a poor strategy 93 94 /*** 95 * @author <a href="mailto:rdonkin@apache.org">Robert Burrell Donkin</a> 96 * @version $Revision: 1.6 $ 97 */ 98 abstract public class AbstractBeanWriter { 99 100 /*** Introspector used */ 101 protected XMLIntrospector introspector = new XMLIntrospector(); 102 103 /*** Log used for logging (Doh!) */ 104 private Log log = LogFactory.getLog( AbstractBeanWriter.class ); 105 /*** Map containing ID attribute values for beans */ 106 protected HashMap idMap = new HashMap(); 107 /*** Used to generate ID attribute values*/ 108 protected IDGenerator idGenerator = new SequentialIDGenerator(); 109 /*** Should generated <code>ID</code> attribute values be added to the elements? */ 110 protected boolean writeIDs = true; 111 112 /*** indentation level */ 113 protected int indentLevel; 114 115 /*** 116 * <p> Writes the given bean to the current stream using the XML introspector.</p> 117 * 118 * <p> This writes an xml fragment representing the bean to the current stream.</p> 119 * 120 * <p>This method will throw a <code>CyclicReferenceException</code> when a cycle 121 * is encountered in the graph <strong>only</strong> if the <code>WriteIDs</code> 122 * property is false.</p> 123 * 124 * @throws CyclicReferenceException when a cyclic reference is encountered 125 * 126 * @param bean write out representation of this bean 127 */ 128 public void write(Object bean) throws IOException, SAXException, IntrospectionException { 129 if (log.isDebugEnabled()) { 130 log.debug( "Writing bean graph..." ); 131 log.debug( bean ); 132 } 133 start(); 134 write( null, bean ); 135 end(); 136 if (log.isDebugEnabled()) { 137 log.debug( "Finished writing bean graph." ); 138 } 139 } 140 141 /*** 142 * Marks the start of the bean writing. 143 * By default doesn't do anything, but can be used 144 * to do extra start processing 145 * @throws IOException 146 * @throws SAXException 147 */ 148 public void start() throws IOException, SAXException { 149 } 150 151 /*** 152 * Marks the start of the bean writing. 153 * By default doesn't do anything, but can be used 154 * to do extra end processing 155 * @throws IOExcpetion 156 * @throws SAXException 157 */ 158 159 public void end() throws IOException, SAXException { 160 } 161 162 163 164 /*** 165 * <p>Writes the given bean to the current stream using the given <code>qualifiedName</code>.</p> 166 * 167 * <p>This method will throw a <code>CyclicReferenceException</code> when a cycle 168 * is encountered in the graph <strong>only</strong> if the <code>WriteIDs</code> 169 * property is false.</p> 170 * 171 * @throws CyclicReferenceException when a cyclic reference is encountered 172 */ 173 public void write( 174 String qualifiedName, 175 Object bean) 176 throws 177 IOException, 178 SAXException, 179 IntrospectionException { 180 181 182 if ( log.isTraceEnabled() ) { 183 log.trace( "Writing bean graph (qualified name '" + qualifiedName + "'" ); 184 } 185 186 // introspect to obtain bean info 187 XMLBeanInfo beanInfo = introspector.introspect( bean ); 188 if ( beanInfo != null ) { 189 ElementDescriptor elementDescriptor = beanInfo.getElementDescriptor(); 190 if ( elementDescriptor != null ) { 191 Context context = new Context( bean, log ); 192 if ( qualifiedName == null ) { 193 qualifiedName = elementDescriptor.getQualifiedName(); 194 } 195 196 String ref = null; 197 String id = null; 198 199 // only give id's to non-primatives 200 if ( elementDescriptor.isPrimitiveType() ) { 201 // write without an id 202 write( 203 qualifiedName, 204 elementDescriptor, 205 context ); 206 } 207 else { 208 209 ref = (String) idMap.get( context.getBean() ); 210 if ( ref == null ) { 211 // this is the first time that this bean has be written 212 AttributeDescriptor idAttribute = beanInfo.getIDAttribute(); 213 if (idAttribute == null) { 214 // use a generated id 215 id = idGenerator.nextId(); 216 idMap.put( bean, id ); 217 218 if ( writeIDs ) { 219 // write element with id 220 write( 221 qualifiedName, 222 elementDescriptor, 223 context , 224 beanInfo.getIDAttributeName(), 225 id); 226 227 } else { 228 // write element without ID 229 write( 230 qualifiedName, 231 elementDescriptor, 232 context ); 233 } 234 235 } else { 236 // use id from bean property 237 // it's up to the user to ensure uniqueness 238 // XXX should we trap nulls? 239 Object exp = idAttribute.getTextExpression().evaluate( context ); 240 if (exp == null) { 241 // we'll use a random id 242 log.debug("Using random id"); 243 id = idGenerator.nextId(); 244 245 } else { 246 // convert to string 247 id = exp.toString(); 248 } 249 idMap.put( bean, id); 250 251 // the ID attribute should be written automatically 252 write( 253 qualifiedName, 254 elementDescriptor, 255 context ); 256 } 257 } 258 else { 259 // we have a cyclic reference 260 if ( !writeIDs ) { 261 // if we're not writing IDs, then throw exception 262 throw new CyclicReferenceException(); 263 } 264 265 // we've already written this bean so write an IDREF 266 writeIDREFElement( 267 qualifiedName, 268 beanInfo.getIDREFAttributeName(), 269 ref); 270 } 271 } 272 } 273 } 274 275 log.trace( "Finished writing bean graph." ); 276 } 277 278 /*** 279 * Get <code>IDGenerator</code> implementation used to generate <code>ID</code> attribute values . 280 * 281 * @return implementation used for <code>ID</code> attribute generation 282 */ 283 public IDGenerator getIdGenerator() { 284 return idGenerator; 285 } 286 287 /*** 288 * Set <code>IDGenerator</code> implementation used to generate <code>ID</code> attribute values. 289 * This property can be used to customize the algorithm used for generation. 290 * 291 * @param idGenerator use this implementation for <code>ID</code> attribute generation 292 */ 293 public void setIdGenerator(IDGenerator idGenerator) { 294 this.idGenerator = idGenerator; 295 } 296 297 /*** Get whether generated <code>ID</code> attribute values should be added to the elements */ 298 public boolean getWriteIDs() { 299 return writeIDs; 300 } 301 302 /*** 303 * Set whether generated <code>ID</code> attribute values should be added to the elements 304 * If this property is set to false, then <code>CyclicReferenceException</code> 305 * will be thrown whenever a cyclic occurs in the bean graph. 306 */ 307 public void setWriteIDs(boolean writeIDs) { 308 this.writeIDs = writeIDs; 309 } 310 311 /*** 312 * <p> Get the introspector used. </p> 313 * 314 * <p> The {@link XMLBeanInfo} used to map each bean is created by the <code>XMLIntrospector</code>. 315 * One way in which the mapping can be customized is by altering the <code>XMLIntrospector</code>. </p> 316 */ 317 public XMLIntrospector getXMLIntrospector() { 318 return introspector; 319 } 320 321 322 /*** 323 * <p> Set the introspector to be used. </p> 324 * 325 * <p> The {@link XMLBeanInfo} used to map each bean is created by the <code>XMLIntrospector</code>. 326 * One way in which the mapping can be customized is by altering the <code>XMLIntrospector</code>. </p> 327 * 328 * @param introspector use this introspector 329 */ 330 public void setXMLIntrospector(XMLIntrospector introspector) { 331 this.introspector = introspector; 332 } 333 334 /*** 335 * <p> Get the current level for logging. </p> 336 * 337 * @return a <code>org.apache.commons.logging.Log</code> level constant 338 */ 339 public Log getLog() { 340 return log; 341 } 342 343 /*** 344 * <p> Set the current logging level. </p> 345 * 346 * @param level a <code>org.apache.commons.logging.Log</code> level constant 347 */ 348 public void setLog(Log log) { 349 this.log = log; 350 } 351 352 353 // Expression methods 354 //------------------------------------------------------------------------- 355 356 /*** Express an element tag start using given qualified name */ 357 abstract protected void expressElementStart(String qualifiedName) throws IOException, SAXException; 358 359 abstract protected void expressTagClose() throws IOException, SAXException; 360 361 /*** Express an element end tag using given qualifiedName */ 362 abstract protected void expressElementEnd(String qualifiedName) throws IOException, SAXException; 363 364 /*** Express an empty element end */ 365 abstract protected void expressElementEnd() throws IOException, SAXException; 366 367 /*** Express body text */ 368 abstract protected void expressBodyText(String text) throws IOException, SAXException; 369 370 /*** Express an attribute */ 371 abstract protected void expressAttribute( 372 String qualifiedName, 373 String value) 374 throws 375 IOException, 376 SAXException; 377 378 379 // Implementation methods 380 //------------------------------------------------------------------------- 381 382 383 /*** Writes the given element */ 384 protected void write( 385 String qualifiedName, 386 ElementDescriptor elementDescriptor, 387 Context context ) 388 throws 389 IOException, 390 SAXException, 391 IntrospectionException { 392 393 if (elementDescriptor.isWrapCollectionsInElement()) { 394 expressElementStart( qualifiedName ); 395 } 396 397 writeRestOfElement( qualifiedName, elementDescriptor, context); 398 } 399 400 401 402 /*** Writes the given element adding an ID attribute */ 403 protected void write( 404 String qualifiedName, 405 ElementDescriptor elementDescriptor, 406 Context context, 407 String idAttribute, 408 String idValue ) 409 throws 410 IOException, 411 SAXException, 412 IntrospectionException { 413 414 expressElementStart( qualifiedName ); 415 416 expressAttribute( idAttribute, idValue ); 417 418 writeRestOfElement( qualifiedName, elementDescriptor, context ); 419 } 420 421 /*** Write attributes, child elements and element end */ 422 protected void writeRestOfElement( 423 String qualifiedName, 424 ElementDescriptor elementDescriptor, 425 Context context ) 426 throws 427 IOException, 428 SAXException, 429 IntrospectionException { 430 431 if (elementDescriptor.isWrapCollectionsInElement()) { 432 writeAttributes( elementDescriptor, context ); 433 } 434 435 if ( writeContent( elementDescriptor, context ) ) { 436 if (elementDescriptor.isWrapCollectionsInElement()) { 437 expressElementEnd( qualifiedName ); 438 } 439 } 440 else { 441 if (elementDescriptor.isWrapCollectionsInElement()) { 442 expressElementEnd(); 443 } 444 } 445 } 446 447 448 449 450 protected void writeIDREFElement( 451 String qualifiedName, 452 String idrefAttributeName, 453 String idrefAttributeValue ) 454 throws 455 IOException, 456 SAXException, 457 IntrospectionException { 458 459 // write IDREF element 460 expressElementStart( qualifiedName ); 461 462 expressAttribute( idrefAttributeName, idrefAttributeValue ); 463 464 expressElementEnd(); 465 } 466 467 /*** Writes the element content. 468 * 469 * @return true if some content was written 470 */ 471 protected boolean writeContent( 472 ElementDescriptor elementDescriptor, 473 Context context ) 474 throws 475 IOException, 476 SAXException, 477 IntrospectionException { 478 ElementDescriptor[] childDescriptors = elementDescriptor.getElementDescriptors(); 479 boolean writtenContent = false; 480 if ( childDescriptors != null && childDescriptors.length > 0 ) { 481 // process child elements 482 for ( int i = 0, size = childDescriptors.length; i < size; i++ ) { 483 ElementDescriptor childDescriptor = childDescriptors[i]; 484 Context childContext = context; 485 Expression childExpression = childDescriptor.getContextExpression(); 486 if ( childExpression != null ) { 487 Object childBean = childExpression.evaluate( context ); 488 if ( childBean != null ) { 489 String qualifiedName = childDescriptor.getQualifiedName(); 490 // XXXX: should we handle nulls better 491 if ( childBean instanceof Iterator ) { 492 for ( Iterator iter = (Iterator) childBean; iter.hasNext(); ) { 493 Object object = iter.next(); 494 if (object == null) { 495 continue; 496 } 497 if ( ! writtenContent ) { 498 writtenContent = true; 499 if (elementDescriptor.isWrapCollectionsInElement()) { 500 expressTagClose(); 501 } 502 } 503 ++indentLevel; 504 write( qualifiedName, object ); 505 --indentLevel; 506 } 507 } 508 else { 509 if ( ! writtenContent ) { 510 writtenContent = true; 511 expressTagClose(); 512 } 513 ++indentLevel; 514 write( qualifiedName, childBean ); 515 --indentLevel; 516 } 517 } 518 } 519 else { 520 if ( ! writtenContent ) { 521 writtenContent = true; 522 expressTagClose(); 523 } 524 if (childDescriptor.isWrapCollectionsInElement()) { 525 ++indentLevel; 526 } 527 528 write( childDescriptor.getQualifiedName(), childDescriptor, childContext ); 529 530 if (childDescriptor.isWrapCollectionsInElement()) { 531 --indentLevel; 532 } 533 } 534 } 535 if ( writtenContent ) { 536 writePrintln(); 537 writeIndent(); 538 } 539 } 540 else { 541 // evaluate the body text 542 Expression expression = elementDescriptor.getTextExpression(); 543 if ( expression != null ) { 544 Object value = expression.evaluate( context ); 545 if ( value != null ) { 546 String text = value.toString(); 547 if ( text != null && text.length() > 0 ) { 548 if ( ! writtenContent ) { 549 writtenContent = true; 550 expressTagClose(); 551 } 552 expressBodyText(text); 553 } 554 } 555 } 556 } 557 return writtenContent; 558 } 559 560 /*** Writes the attribute declarations */ 561 protected void writeAttributes( 562 ElementDescriptor elementDescriptor, 563 Context context ) 564 throws 565 IOException, SAXException { 566 if (!elementDescriptor.isWrapCollectionsInElement()) 567 return; 568 569 AttributeDescriptor[] attributeDescriptors = elementDescriptor.getAttributeDescriptors(); 570 if ( attributeDescriptors != null ) { 571 for ( int i = 0, size = attributeDescriptors.length; i < size; i++ ) { 572 AttributeDescriptor attributeDescriptor = attributeDescriptors[i]; 573 writeAttribute( attributeDescriptor, context ); 574 } 575 } 576 } 577 578 579 /*** Writes an attribute declaration */ 580 protected void writeAttribute( 581 AttributeDescriptor attributeDescriptor, 582 Context context ) 583 throws 584 IOException, SAXException { 585 Expression expression = attributeDescriptor.getTextExpression(); 586 if ( expression != null ) { 587 Object value = expression.evaluate( context ); 588 if ( value != null ) { 589 String text = value.toString(); 590 if ( text != null && text.length() > 0 ) { 591 expressAttribute(attributeDescriptor.getQualifiedName(), text); 592 } 593 } 594 } 595 } 596 597 protected void writePrintln() throws IOException {} 598 protected void writeIndent() throws IOException {} 599 }

This page was automatically generated by Maven